package ar.edu.utn.frba.tacs.warnes.utils;

import java.util.ArrayList;
import java.util.List;

import ar.edu.utn.frba.tacs.warnes.Auto;
import ar.edu.utn.frba.tacs.warnes.Reserva;
import ar.edu.utn.frba.tacs.warnes.Venta;
import ar.edu.utn.frba.tacs.warnes.pedido.Pedido;
import ar.edu.utn.frba.tacs.warnes.pieza.Pieza;
import ar.edu.utn.frba.tacs.warnes.pieza.PiezaDisponibleState;
import ar.edu.utn.frba.tacs.warnes.utils.ConstantesWarnes.Categoria;
import ar.edu.utn.frba.tacs.warnes.utils.ConstantesWarnes.LogLevel;
import ar.edu.utn.frba.tacs.warnes.utils.ConstantesWarnes.PiezaState;

public class ContenedorWarnes {

	private static ContenedorWarnes instancia;
	private List<Pieza> piezas = new ArrayList<Pieza>();
	private List<Auto> autos = new ArrayList<Auto>();
	private List<Reserva> reservas = new ArrayList<Reserva>();
	private List<Venta> ventas = new ArrayList<Venta>();
	private List<Pedido> pedidos = new ArrayList<Pedido>();
	private String rutaLog;
	private LogLevel logLevel = ConstantesWarnes.LogLevel.INFO;
	
	public static ContenedorWarnes getInstance() {
		if (instancia == null) {
			instancia = new ContenedorWarnes();
		}
		return instancia;
	}
	
	private ContenedorWarnes(){
		Pieza.cambiarMoneda(ConstantesWarnes.PESO);
	}
	
	public List<Pedido> getPedidos() {
		return this.pedidos;
	}

	public void setPedidos(List<Pedido> pedidos) {
		this.pedidos = pedidos;
	}

	public void setPiezas(List<Pieza> piezas) {
		this.piezas = piezas;
	}

	public List<Pieza> getPiezas() {
		return this.piezas;
	}

	private void addPieza(Pieza pieza) {
		if (this.getPiezas() == null) {
			this.setPiezas(new ArrayList<Pieza>());
		}
		this.getPiezas().add(pieza);
	}

	/**
	 * Se comporta como una Factory de Piezas.
	 * Crea una nueva Pieza, la cual agrega a su coleccion correspondiente.
	 * La pieza nueva va a tener un estado <b>DISPONIBLE</b>
	 * (el seteo del id a la Pieza es temporal, despues de esto se va a encargar Hibernate)
	 * @return Pieza
	 */
	public Pieza creaUnaPieza() {
		return this.creaUnaPieza("");
	}

	/**
	 * Crea una pieza con descripcion
	 * @param descripcion
	 * @return Pieza
	 */
	public Pieza creaUnaPieza(String descripcion) {
		Pieza pieza = new Pieza(descripcion);
		this.addPieza(pieza);
		pieza.cambiarEstado(PiezaDisponibleState.getInstance());
//		pieza.setId(new Long(this.getPiezas().size()));//TODO despues borrar esta linea
		return pieza;
	}
	
	/**
	 * Esta consulta devuelve las Piazas para una categoria dada.
	 * @param categoria
	 * @return List<Pieza>
	 */
	public List<Pieza> getPiezas(Categoria categoria) {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (categoria.equals(pieza.getCategoria())) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	public void setAutos(List<Auto> autos) {
		this.autos = autos;
	}

	public List<Auto> getAutos() {
		return this.autos;
	}

	private void addAuto(Auto auto) {
		if (this.getAutos() == null) {
			this.setAutos(new ArrayList<Auto>());
		}
		this.getAutos().add(auto);
	}
	
	/**
	 * Se comporta como una Factory de Autos.
	 * Crea un nuevo Auto, la cual agrega a su coleccion correspondiente.
	 * @return Auto
	 */
	public Auto creaUnAuto() {
		return this.creaUnAuto("");
	}
	
	/**
	 * Crea un nuevo Auto con patente.
	 * @return Auto
	 */
	public Auto creaUnAuto(String patente) {
		Auto auto = new Auto(patente);
		this.addAuto(auto);
		return auto;
	}
	
	/** 
	 * Vacia la coleccion de Piezas y de Autos del Contenedor.
	 */
	public void limpiate() {
		this.setPiezas(new ArrayList<Pieza>());
		this.setAutos(new ArrayList<Auto>());
	}

	/**
	 * Esta consulta devuelve las Piazas que esten en un estado determinado.
	 * @param estado
	 * @return List<Pieza>
	 */
	public List<Pieza> getPiezas(PiezaState state) {
		List<Pieza> piezas;
		switch (state.getValue()) {
		case ConstantesWarnes.PIEZA_DISPONIBLE:
			piezas = this.getPiezasDisponibles();
			break;
		case ConstantesWarnes.PIEZA_RESERVADA:
			piezas = this.getPiezasReservada();	
			break;
		case ConstantesWarnes.PIEZA_VENDIDA:
			piezas = this.getPiezasVendidas();	
			break;
		default:
			piezas = new ArrayList<Pieza>();
			break;
		}
		return piezas;
	}

	private List<Pieza> getPiezasDisponibles() {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (pieza.estaDisponible()) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	private List<Pieza> getPiezasReservada() {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (pieza.estaReservada()) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	private List<Pieza> getPiezasVendidas() {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (pieza.estaVendida()) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	/**
	 * Esta consulta devuelve una coleccion con 0 o 1 piezas segun el id indicado.
	 * Devuelve una lista para no romper el polimorfismo con los demas getPiezas()
	 * @param id
	 * @return List<Pieza> con 0 o 1 elemento
	 */
	public List<Pieza> getPiezas(Long id) {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (id.equals(pieza.getId())) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	/**
	 * Esta consulta devuelve una coleccion con 0 o 1 autos segun el id indicado.
	 * Devuelve una lista para no romper el polimorfismo con los demas getAutos()
	 * @param id
	 * @return List<Pieza> con 0 o 1 elemento
	 */
	public List<Auto> getAutos(Long id) {
		List<Auto> autosBuscadas = new ArrayList<Auto>();
		for (Auto auto : this.autos) {
			if (id.equals(auto.getId())) {
				autosBuscadas.add(auto);
			}
		}
		return autosBuscadas;
	}
	
	/**
	 * Se comporta como una Factory de Reservas.
	 * Crea una nueva Reserva, la cual agrega a su coleccion correspondiente.
	 * @return Reserva
	 */
	public Reserva nuevaReserva() {
		Reserva res = new Reserva();
		this.addReserva(res);
		return res;
	}

	public void setReservas(List<Reserva> reservas) {
		this.reservas = reservas;
	}

	public List<Reserva> getReservas() {
		return this.reservas;
	}

	/**
	 * Agrega una reserva al sistema, 
	 * la cual contendra las piezas que se quieren reservar
	 * @param res
	 */
	private void addReserva(Reserva res) {
		if (this.getReservas() == null) {
			this.setReservas(new ArrayList<Reserva>());
		}
		this.getReservas().add(res);
	}

	public Venta nuevaVenta() {
		Venta ven = new Venta();
		this.addVenta(ven);
		return ven;
	}

	public void setVenta(List<Venta> venta) {
		this.ventas = venta;
	}

	public List<Venta> getVenta() {
		return this.ventas;
	}

	/**
	 * Agrega una venta al sistema, 
	 * la cual contendra las piezas que se quieren vender
	 * @param res
	 */
	private void addVenta(Venta ven) {
		if (this.getVenta() == null) {
			this.setVenta(new ArrayList<Venta>());
		}
		this.getVenta().add(ven);
	}
	
	/**
	 * Se comporta como una Factory de Pedidos.
	 * Crea un nuevo Pedido, la cual agrega a su correspondiente coleccion.
	 * @return Pedido
	 */
	public Pedido crearUnPedido(List<Pieza> piezas) {
		Pedido pedido = new Pedido(piezas);
		this.addPedido(pedido);
		return pedido;
	}

	/**
	 * Agrega un pedido al sistema, 
	 * la cual contendra las piezas que se quieren reservar
	 * @param res
	 */
	private void addPedido(Pedido ped) {
		if (this.getPedidos() == null) {
			this.setPedidos(new ArrayList<Pedido>());
		}
		this.getPedidos().add(ped);
	}

	public boolean tenesLaPiezaConId(Long id) {
		boolean resultado = false;
		for (Pieza pieza : this.getPiezas()) {
			if(new Long(pieza.getId()).intValue() == id.intValue()){
				resultado = true;
				break;
			}
		}
		return resultado;
	}

	public String getRutaLog() {
		return this.rutaLog;
	}

	public void setRutaLog(String rutaLog) {
		this.rutaLog = rutaLog;
	}

	public void setLogLevel(LogLevel logLevel) {
		this.logLevel = logLevel;
	}

	public LogLevel getLogLevel() {
		return this.logLevel;
	}
}
